/*
 * threads.c
 *
 *  Created on: 20/10/2011
 *      Author: Normandia
 */

#include "threads.h"
#include "serverRaid.h"


void obtener_fecha(){

	char * cadena;
	char szFecha[16]; // MMM DD HH:MM:SS (formato de fecha a guardar)
	time_t t;      	  // variable para obtener la fecha
	int i;

	t=time(NULL); // obtiene la fecha

	cadena=strchr(asctime(localtime(&t)),' ') + 1;

	for( i=0;i<=14;i++)
	  szFecha[i]=cadena[i];

	szFecha[i]='\0';

	printf("%s \n",szFecha);
}
void despacharColaDisco(void* disco_param){

	REG_DISCO* disco;
	REG_DISCO* disco_maestro;
	REG_COLA_PETICION *pedido,*pedido_dist;
	T_MENSAJE *respuesta,*write_pedido, *read_pedido;
	T_MSG_LECTURA_RTA* msg_rta;
	int socket_rta;
	uint32_t i,j;


	disco = (REG_DISCO*) disco_param;
	collection_list_add(lista_discos,(void*)disco);

    // Proceso de pedidos de PDD
	if(disco->id_disco != 0){//si no es el disco master

		if(parametros.flag == 1) {
			printf("Inicio de sincronización del disco %u: ",disco->id_disco);
			obtener_fecha();
		}

		disco_maestro = (REG_DISCO*) collection_list_get(lista_discos,0);
		log("RAID", "log.txt", "INFO","Atendiendo pedidos de la lista de discos\n");

		if(collection_list_size(lista_discos)>2){
			log("RAID", "log.txt", "INFO","Hay mas de dos discos conectados\n");
			disco_maestro->flag_sinc = 0; //bloqueo para sincronizar(copiar el maestro a disco)
            disco->flag_sinc =  0;// bloqueo(preguntar porq estaba en 0)
        }

		for(j=0;j<discoMaestro.lengthDisco;j=j+BLOQUE_SINCRO){

    		for(i=j;i<j+BLOQUE_SINCRO;i++){
    			read_pedido = mensaje_lectura(i,i);

				if(enviarPorSocket(disco_maestro->socket,read_pedido)) {//->mensaje mando una lectura al maestro
					printf("Error al enviar sector %u\n",i);
					perror("enviarPorSocket");
					return;
				}
				eliminarMensaje(read_pedido);
			}

    		for(i=0;i<BLOQUE_SINCRO;i++){

    			if(recibirDeSocket(disco_maestro->socket, &respuesta)){
					printf("Error al recibir sector %u\n",i+j);
    				perror("recibirDeSocket");
					return;
				}

				msg_rta = respuesta->mensaje;
				write_pedido = mensaje_RaidCopy((uint32_t)msg_rta->id_pedido,msg_rta->id_pedido,msg_rta->data);

				if(enviarPorSocket(disco->socket,write_pedido)) {
					printf("Error al escribir sector %u\n",i);
					perror("enviarPorSocket");
					return;
				}
				eliminarMensaje(respuesta);
				eliminarMensaje(write_pedido);
    		}

        }
    	disco_maestro->flag_sinc = 1; //desbloqueado
    	sem_post(&disco_maestro->flag);

    	respuesta = generarMensaje(TIPO_END_SINCRO, 0,NULL);
    	if(enviarPorSocket(disco->socket,write_pedido)) {
			perror("enviarPorSocket");
		}
		eliminarMensaje(respuesta);

		if(parametros.flag == 1) {
			printf("Fin de sincronización del disco %u: ",disco->id_disco);
			obtener_fecha();
		}
    }

	disco->flag_sinc = 1;//desbloqueo
	disco->flag_fin = 1;

   //Procesando  pedidos de FS
	while(1){
		log("RAID", "log.txt", "INFO","Procesando pedidos del FS\n");
		sem_wait(&disco->contador_elem);	//decrementa un pedido de cola de discos
		sem_wait(&disco->mutex);			//bloquea el socket de copia del maestro(discos planificador)

		pedido = (REG_COLA_PETICION*) collection_queue_pop(disco->cola_pedidos);

		if(enviarPorSocket(disco->socket,pedido->mensaje)) {
			disco->flag_fin = 0;//se cayo el disco
			perror("enviarPorSocket");
			break;
		}
		if(recibirDeSocket(disco->socket, &respuesta)){
			disco->flag_fin = 0;//se cayo el disco
			perror("recibirDeSocket");
			break;
		}

		socket_rta = buscaSocketRta(pedido->id_peticion);
		if(enviarPorSocket(socket_rta,respuesta)) {
			perror("enviarPorSocket");
		}

		eliminarPeticion(pedido); //Elimina peticion de la cola de pedidos
		sem_post(&disco->mutex);// libera el socket de copia del maestro

    }
	if((disco->flag_fin == 0)) {
		close(disco->socket);
		t_queue *cola_disco = disco->cola_pedidos;
		collection_list_removeByPointer(lista_discos,disco,free);
		if(pedido->tipo == TIPO_READSECTOR){
			log("RAID", "log.txt", "INFO","Insertando un pedido de lectura en la COLA de LECTURA del proceso RAID\n");
			collection_queue_push(cola_lectura, (void*) pedido);
		}else{
			eliminarPeticion(pedido); //Elimina peticion de la cola de pedidos
		}

		while((disco->flag_fin == 0) && (collection_queue_size(cola_disco)>0)){
			log("RAID", "log.txt", "INFO","El disco se cayo, agrega las lecturas a la lista de discos\n");
			pedido_dist = (REG_COLA_PETICION*) collection_queue_pop(cola_disco);

			if(pedido_dist->tipo == TIPO_READSECTOR){
				log("RAID", "log.txt", "INFO","Insertando un pedido de lectura en COLALECTURA\n");
				collection_queue_push(cola_lectura, (void*) pedido_dist);
			}else{
				eliminarPeticion(pedido); //Elimina peticion de la cola de pedidos
			}
		}
	}
}

void asignarThreadADisco(REG_DISCO* disco) {

	pthread_t t_id;

	if(pthread_create(&t_id, NULL, (void*)&despacharColaDisco, (void*)disco)) {
		log("RAID", "log.txt", "ERROR","No se pudo crear el trhead para el disco\n");
		printf("No se pudo crear el thread para el disco:%i\n",disco->id_disco);
	}
	//printf("Creado thread: %int/n",t_id);
	disco->thread_id = t_id;
}

